// scaup - Scala up!
// (c) 2008-2010, Normen Müller, normen.mueller@gmail.com  
// $Id: iterators.scala 80 2010-05-13 12:31:58Z normen.mueller $
package scaup.data

/** Implements filtered mappings over <code>scala.Iterator[T]</code>.
 * 
 *  @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 *  @version 0.0.5
 * 
 *  @param s iterator to enclose/ extend
 */
private [scaup] class IteratorExtras[T](s: Iterator[T]) {
  /** Given the partial function <code>f</code>, filter out elements over which <code>f</code> is undefined,
   *  and map the remaining elements with <code>f</code>.
   * 
   *  <p><i>Note:</i> This evaluates <code>f</code> twice for each element.</p>
   */
  def filterMap[B](f: PartialFunction[T,B]) = s filter (f.isDefinedAt) map f
  
  /** Given the function <code>f</code>, filter out elements for which <code>f</code> returns <code>None</code>,
   *  and map the remaining elements with <code>f</code>, extracting the value from the <code>Some</code>.
   * 
   *  <p><i>Note</i> This evaluates <code>f</code> once for each element.</p>
   */  
  def filterMapOption[B](f: Function[T,Option[B]]) = s map f filter (None !=) map (_.get)
  
  /** Given the partial predicate <code>p</code>, find and return the first element over which <code>p</code>
   *  is defined, if any.
   */
  def \?(p: PartialFunction[T,Boolean]): Option[T] = s find (p.isDefinedAt)
  
  /** Given the partial predicate <code>p</code>, apply <code>p</code> over which it is defined and return true,
   *  iff there is at least one element for which <code>p</code> yields <code>true</code>.
   */
  def \!(p: PartialFunction[T,Boolean]): Boolean = s exists (p.isDefinedAt)  
}

/**
 *  @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 *  @version 0.0.5
 * 
 *  @todo refactor to IteratorExtras
 */
object IteratorHelp {  
  /** Keeps running the block until it returns null.
   */
  def nonNull[A <: AnyRef](f: => A) = new Iterator[A] {
    private var n: A = _
    private var h = false
    def hasNext = {
      if(!h) { n = f; h = true }
      n != null
    }
    def next = {
      if(!h) n = f
      h = false
      n
    }
  }
  
  /** Keeps running the block until it returns a negative number.
   */
  def nonNegative(f: => Int) = new Iterator[Int] {
    private var n = 0
    private var h = false
    def hasNext = {
      if(!h) { n = f; h = true }
      n >= 0
    }
    def next = {
      if(!h) n = f
      h = false
      n
    }
  }  
}
